/*
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.randis.dailyplanner;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author Alexei Petinov
 */
public class DispatcherServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private String  				controllerPackage;

    private Map<String, Object>     controllers = Collections.synchronizedMap(new HashMap<String, Object>());

    @Override
    public void init(ServletConfig config) throws ServletException {
        controllerPackage = config.getInitParameter("controllerPackage");
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
                                                                                            IOException {
        String      pathInfo            = request.getPathInfo();
        String[]    parts               = pathInfo.split("/");
        String      controllerName      = parts[parts.length - 2];
        String      methodName          = parts[parts.length - 1];

        try {
            Object controller = getController(controllerName);
            invokeMethod(controller, methodName, request, response);
        } catch (Exception ex) {
            Logger.getLogger(DispatcherServlet.class.getName()).log(Level.SEVERE, null, ex);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        } 
    }

    private synchronized Object getController(String controllerName) throws ClassNotFoundException, 
                                                                            InstantiationException,
                                                                            IllegalAccessException {
        Object controller = controllers.get(controllerName);

        if (controller == null) {
            String  capitalizedName         =   controllerName.substring(0, 1).toUpperCase() +
                                                controllerName.substring(1, controllerName.length());
            String  controllerClassName     =   controllerPackage + "." + capitalizedName + "Controller";
            Class<?>   controllerClass      =   Class.forName(controllerClassName);

            controller = controllerClass.newInstance();
            controllers.put(controllerName, controller);
            return controller;
        }
        else
            return controller;
    }

    private void invokeMethod(  Object              controller,
                                String              methodName,
                                HttpServletRequest  request,
                                HttpServletResponse response) throws    NoSuchMethodException,
                                                                        IllegalAccessException,
                                                                        InvocationTargetException,
                                                                        ServletException,
                                                                        IOException {

            Method method = controller.getClass().getMethod(methodName,
                                                            HttpServletRequest.class,
                                                            HttpServletResponse.class);
            ModelAndView modelAndView = (ModelAndView)method.invoke(controller, request, response);

            if (modelAndView == null)
            	return;
            
            RequestDispatcher dispatcher = request.getRequestDispatcher(modelAndView.getViewName());
            Map<String, Object> model = modelAndView.getModel();
            for (String param : model.keySet()) {
                request.setAttribute(param, model.get(param));
            }

            dispatcher.forward(request, response);
    }
}